package com.xhy.documents_collection.utils;

import com.aliyun.oss.*;
import com.aliyun.oss.model.*;
import com.xhy.documents_collection.entity.res.OSSRes;
import com.xhy.documents_collection.exception.BaseException;
import com.xhy.documents_collection.exception.OssException;
import com.xhy.documents_collection.holder.UserHolder;
import com.xhy.documents_collection.entity.DO.FileDO;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Author: Xhy
 * CreateTime: 2022-11-19 23:20
 */
public class OSSUtil {


    private static String endpoint = "your endpoint";
    private static String accessKeyId = "your accessKeyId";
    private static String accessKeySecret = "your accessKeySecret";
    private static String bucketName = "your bucketName";

    private final static String url = "https://" + bucketName + "." + endpoint + "/";

    final static String filePath = "临时压缩文件地址/";

    // linux:用于下载到本地
    static String localFilePath = "/xhyTempFiles/";

    static {
        String property = System.getProperty("os.name");
        if (property.equals("Windows 10")){
            localFilePath = "D:\\桌面\\临时压缩文件地址\\";
        }
    }
    /**
     * 上传文件
     * @param fileDO
     * @param filePath 保存的文件路径+文件名,例如:file/xhy.txt
     */
    public static String upload(String filePath, FileDO fileDO){

        // 填写Object完整路径，完整路径中不能包含Bucket名称，例如exampledir/exampleobject.txt。
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            // 获取文件流
            // 创建PutObject请求。
            ossClient.putObject(bucketName, filePath, fileDO.getInputStream());
        } catch (OSSException oe) {
            throw new OssException(500,"文件上传出错");
        } catch (ClientException ce) {
            throw new OssException(500,"文件上传出错");
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return filePath;
    }


    /**
     * 删除文件或目录。如果要删除目录，目录必须为空。
     * @param filePath 文件目录/路径
     */
    public static void deleteFile(String filePath){
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            // 删除文件或目录。如果要删除目录，目录必须为空。
            ossClient.deleteObject(bucketName, filePath);
        } catch (OSSException oe) {
           throw new OssException(500,"文件删除出错");
        } catch (ClientException ce) {
            throw new OssException(500,"文件删除出错");
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    public static R deleteFiles(List<String> filePaths){
        Integer uId = UserHolder.get();
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            // 删除文件或目录。如果要删除目录，目录必须为空。
            ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(filePaths));
        } catch (OSSException oe) {
            oe.printStackTrace();
            return R.error().message(oe.toString());
        } catch (ClientException ce) {
            ce.printStackTrace();
            return R.error().message(ce.toString());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return R.ok();
    }

    // 删除目录
    public static void deleteCatalogue(String catalogue){
        Integer uId = UserHolder.get();
        // 如果您仅需要删除src目录及目录下的所有文件，则prefix设置为src/。
        // String prefix = "src/";
        catalogue+="/";
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        try {
            // 列举所有包含指定前缀的文件并删除。
            String nextMarker = null;
            ObjectListing objectListing = null;
            do {
                ListObjectsRequest listObjectsRequest = new ListObjectsRequest(bucketName)
                        .withPrefix(catalogue)
                        .withMarker(nextMarker);
                objectListing = ossClient.listObjects(listObjectsRequest);
                if (objectListing.getObjectSummaries().size() > 0) {
                    List<String> keys = new ArrayList<String>();
                    for (OSSObjectSummary s : objectListing.getObjectSummaries()) {
                        keys.add(s.getKey());
                    }
                    DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName).withKeys(keys).withEncodingType("url");
                    ossClient.deleteObjects(deleteObjectsRequest);
                }
                nextMarker = objectListing.getNextMarker();
            } while (objectListing.isTruncated());
        } catch (OSSException oe) {

            return;
        } catch (ClientException ce) {
            return;

        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }



    public static void getFilesToLocalZip(List<String> fileNames){
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);
        String zipName = UUID.randomUUID().toString()+".zip";
        //创建临时文件
        File zipFile = null;
        try {
            zipFile = File.createTempFile("test", ".zip");
            FileOutputStream fileOutputStream = new FileOutputStream(zipFile);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 下载oss文件打包成压缩包
    public static OSSRes downloadFilesToZipLocal(List<String> fileNames){
        OSS ossClient = null;
        String suffix = UUID.randomUUID().toString()+".zip";
        String path = localFilePath +suffix ;
        InputStream inputStream = null;
        ZipOutputStream zos = null;
        File zipFile = null;
        String errorFileName = "";
        try {
            ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            //创建临时文件
            zipFile = new File(path);
            FileOutputStream fileOutputStream = new FileOutputStream(zipFile);
            CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream, new Adler32());
            //用于将数据压缩成zip文件格式
            zos = new ZipOutputStream(cos);
            for (String fileName : fileNames) {
                errorFileName = fileName;
                //获取object，返回结果ossObject对象
                OSSObject ossObject = ossClient.getObject(bucketName, fileName);
                //读取object内容，返回
                inputStream = ossObject.getObjectContent();
                // 对于每一个要被存放到压缩包的文件，都必须调用ZipOutputStream对象的putNextEntry()方法，确保压缩包里面文件不同名
                zos.putNextEntry(new ZipEntry(fileName));
                int bytesRead = 0;
                // 向压缩文件中输出数据
                while ((bytesRead = inputStream.read()) != -1) {
                    zos.write(bytesRead);
                }
                inputStream.close();
                zos.closeEntry(); // 当前文件写完，定位为写入下一条项目
            }
        }catch (Exception e){
            fileNames.remove(errorFileName);
            downloadFilesToZipLocal(fileNames);
            return OSSRes.failed(errorFileName);
        }finally {
            try {
                zos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            ossClient.shutdown();
        }
        return OSSRes.ok(path);
    }

    /**
     * 下载oss文件打包成压缩包再上传oss返回url
     * @param fileNames
     */
    public static String downloadFilesToZipOss(List<String> fileNames) {
        String filePath = "";
        OSS ossClient = null;
        try {
            ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);
            //创建临时文件
            File zipFile = File.createTempFile("test", ".zip");
            FileOutputStream fileOutputStream = new FileOutputStream(zipFile);

            /**
             * 作用是为任何outputstream产生校验和
             * 第一个参数是制定产生校验和的输出流，第二个参数是指定checksum类型（Adler32（较快）和CRC32两种）
             */
            CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream, new Adler32());
            //用于将数据压缩成zip文件格式
            ZipOutputStream zos = new ZipOutputStream(cos);
            for (String fileName : fileNames) {
                //获取object，返回结果ossObject对象
                OSSObject ossObject = ossClient.getObject(bucketName, fileName);
                //读取object内容，返回
                InputStream inputStream = ossObject.getObjectContent();
                // 对于每一个要被存放到压缩包的文件，都必须调用ZipOutputStream对象的putNextEntry()方法，确保压缩包里面文件不同名
                zos.putNextEntry(new ZipEntry(fileName));
                int bytesRead = 0;
                // 向压缩文件中输出数据
                while ((bytesRead = inputStream.read()) != -1) {
                    zos.write(bytesRead);
                }
                inputStream.close();
                zos.closeEntry(); // 当前文件写完，定位为写入下一条项目
            }
            zos.close();
            FileInputStream fis = new FileInputStream(zipFile);
            // 上传oss
            filePath = upload(fis);
            // 关闭流
            fis.close();
            // 删除临时文件
            zipFile.delete();
        } catch (Exception e) {
            return "";
        }finally {
            ossClient.shutdown();
        }
        return filePath;
    }
    public static String upload(FileInputStream inputStream){

        // 填写Object完整路径，完整路径中不能包含Bucket名称，例如exampledir/exampleobject.txt。
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        String path = filePath + UUID.randomUUID().toString() + ".zip";
        try {
            // 获取文件流
            // 创建PutObject请求。
            ossClient.putObject(bucketName, path, inputStream);
        } catch (OSSException oe) {
            return "";
        } catch (ClientException ce) {
            return "";
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }

        return path;

    }

    public static String uploadTaskDescription(FileDO fileDO) {
        InputStream inputStream = fileDO.getInputStream();
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        String filePath = fileDO.getPath();
        try {
            // 获取文件流
            // 创建PutObject请求。
            ossClient.putObject(bucketName, filePath, inputStream);
        } catch (OSSException oe) {
           throw new OssException(500,"上传作业描述出错了");

        } catch (ClientException ce) {
            throw new OssException(500,"上传作业描述出错了");
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return filePath;
    }


    public static void downloadFile(String filePath, HttpServletResponse response) {
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        OSSObject ossObject = ossClient.getObject(bucketName, filePath);
        try (InputStream fis = ossObject.getObjectContent(); OutputStream fos = response.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream(fos)) {
            response.setContentType("application/octet-stream; charset=UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + new String(filePath.getBytes("UTF-8"), "ISO-8859-1"));
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            bos.flush();
        } catch (Exception e){
            throw new BaseException(500,"下载失败");
        }finally {
            ossClient.shutdown();
        }
    }
}
